/* 
 * Cashbox is the legal property of its developers, whose names are listed in the copyright file included
 * with this source distribution.
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#import "CB50Importer.h"
#import <Cashbox/CBAttribute.h>

#define CB50_ACCOUNT_NAME						@"Account Name"
#define CB50_ACCOUNT_ADD_INFO					@"Additional Account Information"
#define CB50_ACCOUNT_TRANSACTIONS				@"Transactions"

#define CB50_TRANSACTION_CORES_INDEX			@"Index of Coresponding Transaction"
#define CB50_TRANSACTION_CORES_ACCOUNT			@"Name of Coresponding Account"
#define CB50_TRANSACTION_LABEL					@"Label"

#define CB50_TRANSACTION_RECONCILED				@"Reconciled Column"
#define CB50_TRANSACTION_DATE					@"Date Column"
#define CB50_TRANSACTION_DESCRIPTION			@"Description Column"
#define CB50_TRANSACTION_CHECK					@"Check Column"
#define CB50_TRANSACTION_WITHDRAWAL				@"Withdrawal Column"
#define CB50_TRANSACTION_DEPOSIT				@"Deposit Column"

#define CB50_LABEL_NAME							@"Name of Label"
#define CB50_LABEL_COLOR						@"Normal Background Color of Label"
#define CB50_LABEL_RED							@"Red Component"
#define CB50_LABEL_GREEN						@"Green Component"
#define CB50_LABEL_BLUE							@"Blue Component"
#define CB50_LABEL_ALPHA						@"Alpha Component"

// keys used in linking
#define REFERENCE			@"reference"
#define ACCOUNT				@"account"
#define INDEX				@"index"

@interface CB50Importer (Private)

- (void)parseLabel:(NSDictionary *)label;

- (void)parseAccount:(NSDictionary *)account;

- (void)parseTransaction:(NSDictionary *)transaction
				 atIndex:(int)index
		 usedCheckNumber:(BOOL *)usedCheckNumber
			   inAccount:(NSString *)name
			   reference:(id)accountReference;

- (void)shouldLinkReference:(id)reference
   withTransactionInAccount:(NSString *)accountName
					atIndex:(int)index;

- (BOOL)linkReference:(id)reference
	 fromAccountNamed:(NSString *)accountName
			  atIndex:(int)index;

@end

@implementation CB50Importer

- (id)init {
	if (self = [super init]) {
		links = [[NSMutableArray alloc] init];
	}
	return self;
}

- (void)dealloc {
	[links release];
	[super dealloc];
}

- (void)addDataFromFile:(NSString *)basePath {
	NSString *accountsPath = [basePath stringByAppendingPathComponent:@"Accounts"];
	NSString *labelsPath = [basePath stringByAppendingPathComponent:@"Labels.plist"];
	NSEnumerator *labelsEnumerator = [[NSArray arrayWithContentsOfFile:labelsPath] objectEnumerator];
	NSDictionary *label;
	while (label = [labelsEnumerator nextObject]) {
		[self parseLabel:label];
	}
	
	NSString *file;
	NSEnumerator *fileEnumerator = [[[NSFileManager defaultManager] directoryContentsAtPath:accountsPath] objectEnumerator];
	while (file = [fileEnumerator nextObject]) {
		NSDictionary *account;
		if (account = [NSDictionary dictionaryWithContentsOfFile:[accountsPath stringByAppendingPathComponent:file]]) {
			[self parseAccount:account];
		}
	}
}

- (void)parseLabel:(NSDictionary *)label {
	NSString *name = [label objectForKey:CB50_LABEL_NAME];
	NSDictionary *colorDict = [label objectForKey:CB50_LABEL_COLOR];
	NSColor *color = [NSColor colorWithCalibratedRed:[[colorDict objectForKey:CB50_LABEL_RED] floatValue]
											   green:[[colorDict objectForKey:CB50_LABEL_GREEN] floatValue]
												blue:[[colorDict objectForKey:CB50_LABEL_BLUE] floatValue]
											   alpha:[[colorDict objectForKey:CB50_LABEL_ALPHA] floatValue]];
	[super noteLabel:name withColor:color];
}

- (void)parseAccount:(NSDictionary *)account {
	id ref;
	NSString *name = [account objectForKey:CB50_ACCOUNT_NAME];
	NSString *additionalInfo = [account objectForKey:CB50_ACCOUNT_ADD_INFO];
	[super addAccountWithName:name
			   additionalInfo:additionalInfo
						 keys:[[self class] defaultKeys]
					reference:&ref];
	
	int index = 0;
	BOOL accountUsedCheckNumber = FALSE;
	NSDictionary *transaction;
	NSEnumerator *transactionsEnumerator = [[account objectForKey:CB50_ACCOUNT_TRANSACTIONS] objectEnumerator];
	while (transaction = [transactionsEnumerator nextObject]) {
		BOOL usedCheckNumber;
		[self parseTransaction:transaction
					   atIndex:index
			   usedCheckNumber:&usedCheckNumber
					 inAccount:name
					 reference:ref];
		
		// update for whether or not the account used the check number
		// based on whether or not this individual transaciton used the account number
		// or if any other transactions used it in the past
		accountUsedCheckNumber = accountUsedCheckNumber ? accountUsedCheckNumber : usedCheckNumber;
		index++;
	}
	
	if (!accountUsedCheckNumber) {
		NSArray *keys = [NSArray arrayWithObjects:
			[NSNumber numberWithShort:CBDescriptionRole],
			[NSNumber numberWithShort:CBPositiveCashflowRole],
			[NSNumber numberWithShort:CBNegativeCashflowRole], nil];
		[super updateAccountKeys:keys reference:ref];
	}
}

- (void)shouldLinkReference:(id)reference
	 withTransactionInAccount:(NSString *)accountName
					atIndex:(int)index {
	
	NSDictionary *linkInfo = [NSDictionary dictionaryWithObjectsAndKeys:
		reference, REFERENCE,
		accountName, ACCOUNT,
		[NSNumber numberWithInt:index], INDEX, nil];
	[links addObject:linkInfo];
}

- (BOOL)linkReference:(id)reference
	 fromAccountNamed:(NSString *)accountName
			  atIndex:(int)index {
	
	NSDictionary *linkInfo;
	NSEnumerator *linkEnumerator = [links objectEnumerator];
	while (linkInfo = [linkEnumerator nextObject]) {
		if ([accountName isEqualToString:[linkInfo objectForKey:ACCOUNT]] &&
			index == [[linkInfo objectForKey:INDEX] intValue]) {
			break;
		}
	}
	
	if (linkInfo) {
		[super createTransfersFor:reference andFor:[linkInfo objectForKey:REFERENCE]];
		[links removeObject:linkInfo];
		return TRUE;
	} else {
		return FALSE;
	}
}

- (void)parseTransaction:(NSDictionary *)transaction
				 atIndex:(int)index
		 usedCheckNumber:(BOOL *)usedCheckNumber
			   inAccount:(NSString *)accountName
			   reference:(id)accountReference {
	
	id ref;
	BOOL reconciled = [[transaction objectForKey:CB50_TRANSACTION_RECONCILED] boolValue];
	id description = [transaction objectForKey:CB50_TRANSACTION_DESCRIPTION];
	id label = [transaction objectForKey:CB50_TRANSACTION_LABEL];
	long long deposit = roundtol([[transaction objectForKey:CB50_TRANSACTION_DEPOSIT] doubleValue] * 100);
	long long withdrawal = roundtol([[transaction objectForKey:CB50_TRANSACTION_WITHDRAWAL] doubleValue] * 100);
	id date = [NSCalendarDate dateWithString:[[transaction objectForKey:CB50_TRANSACTION_DATE] description]];
	id checkNumberString = [transaction objectForKey:CB50_TRANSACTION_CHECK];
	id checkNumber = [NSNull null];
	if (checkNumberString && ![checkNumberString isEqualToString:@""]) { checkNumber = checkNumberString; }
	
	if (!description) { description = [NSNull null]; }
	if (!label) { label = [NSNull null]; }
	if (!date) { date = [NSNull null]; }
	
	NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
		description, [NSNumber numberWithShort:CBDescriptionRole],
		checkNumber, [NSNumber numberWithShort:CBCheckNumberRole],
		[NSNumber numberWithLongLong:deposit], [NSNumber numberWithShort:CBPositiveCashflowRole],
		[NSNumber numberWithLongLong:withdrawal], [NSNumber numberWithShort:CBNegativeCashflowRole], nil];
		
	[super addTransactionToAccount:accountReference
					withAttributes:attributes
							  date:date
						reconciled:reconciled
						   cleared:NO
						 labelName:label
						 reference:&ref];
	
	if (usedCheckNumber) { *usedCheckNumber = (BOOL)(checkNumberString && ![checkNumberString isEqualToString:@""]); }
	
	BOOL linked = [self linkReference:ref fromAccountNamed:accountName atIndex:index];

	if (!linked) { // link once, don't worry about it if it's already been linked
		NSString *corespondingAccountName = [transaction objectForKey:CB50_TRANSACTION_CORES_ACCOUNT];
		NSNumber *corespondingIndex = [transaction objectForKey:CB50_TRANSACTION_CORES_INDEX];
		if (corespondingAccountName && corespondingIndex) {
			[self shouldLinkReference:ref withTransactionInAccount:corespondingAccountName atIndex:[corespondingIndex intValue]];
		}		
	}
}

@end
